;;;; -*- mode: Scheme; indent-tabs-mode: nil; fill-column: 80; -*-
;;;; 
;;;; Copyright © 2015 Rémi Delrue <asgeir@free.fr>
;;;; 
;;;; This program is free software: you can redistribute it and/or modify
;;;; it under the terms of the GNU General Public License as published by
;;;; the Free Software Foundation, either version 3 of the License, or
;;;; (at your option) any later version.
;;;; 
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;;; GNU General Public License for more details.
;;;; 
;;;; You should have received a copy of the GNU General Public License
;;;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;; The guile-gnunet situation is rather messy,
;; so a customised copy is used. (messy = the old 2015
;; tree is abandoned and has more features and requires
;; some minimal patches for guile2.2 and guile3 support,
;; and the new tree is more recent but has far fewer
;; features). --- Maxime Devos (2020)

(define-module (rehash configuration)
  #:use-module (srfi srfi-9)
  #:use-module (system foreign)
  #:use-module (rnrs bytevectors)
  #:use-module (rehash common)
  #:use-module (rehash binding-utils)
  #:export (<configuration>
            load-configuration
            configuration?
            unwrap-configuration
            configuration-ref
            configuration-value-set?))

(define-record-type <configuration>
  (wrap-configuration pointer)
  configuration?
  (pointer unwrap-configuration))

(define-gnunet %create "GNUNET_CONFIGURATION_create" : '() -> '*)
(define %destroy (dynamic-func "GNUNET_CONFIGURATION_destroy" gnunet-util-ffi))
(define-gnunet %load "GNUNET_CONFIGURATION_load" : '(* *) -> int)

(define-gnunet %get-value-number
  "GNUNET_CONFIGURATION_get_value_number" : '(* * * *) -> int)
(define-gnunet %get-value-string
  "GNUNET_CONFIGURATION_get_value_string" : '(* * * *) -> int)
(define-gnunet %configuration-have-value?
  "GNUNET_CONFIGURATION_have_value"       : '(* * *) -> int)

(define (load-configuration filename)
  "Load GnuNet default configuration (a set of files sometimes placed
in `/usr/share/gnunet/config.d/`), and then the configuration file
denoted by FILENAME. Returns a configuration handle."
  (let ((%handle (%create)))
    (when (eq? %null-pointer %handle)
      (throw 'memory-allocation-error "load-configuration" %create))
    (set-pointer-finalizer! %handle %destroy)
    (when (not (= gnunet-ok (%load %handle (string->pointer filename))))
      (throw 'file-unavailable "load-configuration" filename))
    (wrap-configuration %handle)))

(define (configuration-ref type config section option)
  "Get a configuration value from CONFIG. TYPE must be either `integer` or
`string`."
  (let* ((result (make-bytevector (sizeof int) 0))
         (%result (bytevector->pointer result))
         (get-value-fun (case type
                          ((integer) %get-value-number)
                          ((string)  %get-value-string)
                          (else (throw 'invalid-arg
                                       "configuration-ref" type))))
         (retval (get-value-fun (unwrap-configuration config)
                                (string->pointer* section)
                                (string->pointer* option)
                                %result)))
    (if (and (= gnunet-ok retval)
             (not (eq? %null-pointer result)))
        (case type
          ((integer)
           (bytevector-sint-ref result 0 (native-endianness) (sizeof int)))
          ((string)
           (pointer->string
            (dereference-pointer (bytevector->pointer result)))))
        #f)))

(define (configuration-value-set? config section option)
  (= gnunet-ok
     (%configuration-have-value? (unwrap-configuration config)
                                 (string->pointer* section)
                                 (string->pointer* option))))
